Filter WebGrid With Cascading Dropdownlist Along With Paging in MVC

Introduction

I find most developers new to MVC find it difficult to write the kind of code that provides them a kick-start. This is my article on that. For adding paging to a Grid I have used PagedList.MVC. Agenda

  • Creating an MVC basic application.
  • Adding an ADO.Net entity model to the application.
  • Adding PagedList.MVC to your application.
  • Adding a Home Controller.
  • Adding a View Model (CustomerView).
  • Adding a View.
  • Binding a dropdown to country.
  • Binding a dropdown to states based on the country using JSON.
  • Finally, a binding grid is based on country and state.
  • Final output.

The tool has been used

  • Visual Studio 2012.
  • SQL Server and Entity Framework 5.0.

Tables used for the demo

Tables use

Let's start. Creating MVC basic application For creating the basic application in MVC in Visual Studio IDE select "File" -> "New" -> "Project...". A New Project dialog will be shown. Inside that there is a template list and from that select web and in the project template list select ASP.NET MVC 4 Web Application name your project DemoGridFilter and click on the OK button.

Installed

During the addition of the project, a new dialog will be shown for selecting the Project Template, and inside that select the Basic template.

Basic

After creating the application here is the complete folder view.

Demo grid filter

Now that we have created the application, let us start by adding a PagedList.MVC to the application from the NuGet Package Manager. Adding PagedList.MVC in your application For adding PagedList.MVC just right-click on the application then select Manage NuGet Packages.

Manage NuGet Packages

After selecting Manage NuGet Packages a dialog will be shown. In search type PagedList and then in the search results select the first result PagedList.MVC and click on the Install button.

Install

Here is the view after adding.

View after Adding

After adding the PagedList.MVC let's add an ADO.NET Entity Data Model. Adding an ADO.NET Entity Data Model to the application (.edmx). Procedure to Add Entity Data Model We will add an ADO.NET Entity Data Model in the Model folder.

  1. For adding, right-click on the Model Folder then select Add then inside that select ADO.NET Entity Data Model.
  2. After selecting, a small dialog will be shown to prompt for a name; I am providing the name DBModel. Then click on the OK button.
  3. After clicking on the OK button a new wizard will be shown with the name Entity Data Model Wizard. In that select Generate from Database.
  4. Next, a wizard will be shown for the Connection Properties. Here just enter all the connection-related information for the database that you want to use and then select “Yes include the sensitive data in the connection string”.
  5. Now in the next wizard, it will ask for select tables from the database, and inside that select Country, States, DeveloperDetails then finally click on the Finish button
    DeveloperDetails

After adding the Entity Data Model here is the Designer view of it.

Designer view

After adding the Entity Data Model here is the complete folder view.

Entity Data Model

After adding the ADO.NET Entity Data Model let's add a Controller. Adding Myhome Controller For adding the Controller just right-click on the Controller folder then select Add then select Controller. After selecting, a new dialog will be shown with the name Add Controller. Here we will name the controller MyhomeController and in the scaffolding option in the template select Empty MVC controller.

Controller

The Add Controller dialog snapshot is below.

Add controller

After adding the Controller the default Index ActionResult is generated, here is the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace DemoGridFilter.Controllers
{
    public class MyhomeController : Controller
    {
        // GET: /Myhome/
        public ActionResult Index()
        {
            return View();
        }

    }
}

Now let's move forward and add ViewModel with the name SearchModelVM.

Adding view model (SearchModelVM)

For adding just right-click on the Model folder then select Add then select Class. A new wizard will be shown asking for a class name. Name it SearchModelVM.cs and click on the Add button.

Models

An empty class is generated.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DemoGridFilter.Models
{
    public class SearchModelVM
    {

    }
}

Now let's add some properties to it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using PagedList;
namespace DemoGridFilter.Models
{
    public class SearchModelVM
    {
        public int? Page { get; set; }
        public string DeveloperCount { get; set; }
        public string DevID { get; set; }
        public IPagedList<DeveloperDetail> SearchResults { get; set; }
        public string SearchButton { get; set; }
        public IEnumerable<Country> ListCountry { get; set; }
        public int? SelectedCountryID { get; set; }
        public int? SelectedStateID { get; set; }
    }
}

Now in the preceding code, I took Page for maintaining the paging and Developercount and DevID that I will display in the Grid. Then I took an IPagedList for generating the paging control for the Webgrid. The following SearchButton is for letting us know that the user clicked on the search button. The list of Countries is for binding the dropdown list. And SelectedCountryID and SelectedStateID are for getting the dropdown list's selected value.

Adding view

To add a view just right-click inside the Index ActionResult. A new dialog will be shown with the name Add View and we have a default view name that is the same as the ActionResult Name (Index). We will not be using the scaffolding template when adding the View.

Adding view

A blank view is generated and nowhere is the default code that is generated.

@model DemoGridFilter.Models.SearchModelVM
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>

After adding the View let's add a Bind County and States Dropdownlist on the View.

Following I have Added a method (BindCountriesName) for binding Country and this method I will call in the Index [HttpGet] Action Method.

The following is the code snippet of the BindCountriesName method.

public void BindCountriesName(SearchModelVM model)
{
    List<Country> listCountry = new List<Country> { new Country { CountryID = 0 , Name ="Select" } };
    var entities = new AllSampleCodeEntities();
    var Countrieslist = (from ad in entities.Countries select ad).ToList();
    foreach (var item in Countrieslist)
    {
        Country objcon = new Country();
        objcon.CountryID = item.CountryID;
        objcon.Name = item.Name;
        listCountry.Add(objcon);
    }
    model.ListCountry = listCountry;
}

After completing the Binding Country we will fill in the states depending on the Country and display them in the States dropdown list. For that, we will use the JSON result that will take the CountryID as input, and depending on that it will get all the states from the database and return a JSON result.

Code snippet of GetStates JsonResult

public JsonResult GetStates(string id)
{
    if (id == null)
    {
        id = "0";
    }
    int CountryID = Convert.ToInt32(id);
    AllSampleCodeEntities objord = new AllSampleCodeEntities();
    var states = (from slist in objord.States
                  where (slist.CountryID == CountryID)
                  select new { slist.StateID , slist.StateName}).ToList();
    return Json(new SelectList(states, "StateID", "StateName"));
}     

Now after binding both dropdown lists let's configure the action method (Index) to bind the Webgrid.

Inside the index action method, I wrote an if condition that checks whether the user has clicked on a button or not. If we click on a button then it will execute the method inside. it will get values from the database depending on the selection of country and state and finally, it will fill in a pagedList Collection with the pageIndex and RecordsPerPage Parameters and then send an entire model to the view.

Code snippet of Index ActionResult

const int RecordsPerPage = 1;

public ActionResult Index(SearchModelVM model, FormCollection fc)
{
    BindCountriesName(model);
    if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue)
    {
        ViewData["Selectedstate"] = model.SelectedStateID;
        decimal mprice = Convert.ToDecimal(model.SelectedStateID);
        var entities = new AllSampleCodeEntities();
        var results = entities.DeveloperDetails
            .Where(p => (p.CountryID == model.SelectedCountryID || model.SelectedCountryID == null) && (p.StateID == mprice || mprice == null))
            .OrderBy(p => p.CountryID);
        var pageIndex = model.Page ?? 1;
        model.SearchResults = results.ToPagedList(pageIndex, RecordsPerPage);
    }
    return View("Index", model);
}

After adding an Action method let's move toward configuring a View. We will now add a Model (SearchModelVM) and a dropdown list to the View.

Adding model to view.

@model DemoGridFilter.Models.SearchModelVM
@using PagedList.Mvc
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Add a dropdown list of County and State to the View as in the following.

<table style="border: 0px; width: 500px;">  
        <tr>  
            <td>  
                <div class="editor-label">  
                    @Html.Label("Country")  
                </div>  
                <div class="editor-field">  
                    @Html.DropDownListFor(model => model.SelectedCountryID, new SelectList(Model.ListCountry, "CountryID", "Name"))  
                </div>  
            </td>  
            <td>  
                <div class="editor-label">  
                    @Html.Label("States")  
                </div>  
                <div class="editor-field">  
                    @Html.DropDownList("SelectedStateID", new SelectList(string.Empty, "StateID", "StateName"), "Select State", new { style = "width:250px", @class = "dropdown1" })  
                </div>  
            </td>  
            <td style="vertical-align: bottom;">  
                <input name="SearchButton" type="submit" value="Search" />  
            </td>  
        </tr>  
       </table>  

After adding a Dropdown list, now let's add a jQuery script for binding the state on a change of the country dropdown list.

Binding dropdown states based on the country using JSON.

The following adds a jQuery Ajax script for binding the State drop-down list.

<link href="~/Content/PagedList.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<script>
    $(document).ready(function () {
        //Dropdownlist Selectedchange event
        $("#SelectedCountryID").change(function () {
            $("#SelectedStateID").empty();
            $.ajax({
                type: 'POST',
                url: '@Url.Action("GetStates")', // we are calling json method
                dataType: 'json',
                data: { id: $("#SelectedCountryID").val() },
                success: function (states) {
                    // states contains the JSON formatted list
                    // of states passed from the controller
                    $("#SelectedStateID").append('<option value="' + "0" + '">' + "Select State" + '</option>');
                    debugger;
                    $.each(states, function (i, state) {
                        $("#SelectedStateID").append('<option value="' + state.Value + '">' + state.Text + '</option>');
                        // here we are adding option for States
                    });
                },
                error: function (ex) {
                    alert('Failed to retrieve states.' + ex);
                }
            });
            return false;
        })
    });
</script>

After adding a Dropdownlist and jQuery Ajax script let's run the application and check how your dropdownlist displays.

Here is the View of Index.cshtml after rendering on the browser.

View of Index

We have completed the binding on the dropdown list. Now let's add a Webgrid and Pagedlist to the view.

Adding WebGrid

First I added a Webgrid and then I passed an IPagedList (SearchResults) model to the WebGrid and 1 column (DeveloperCount).

var grid = new WebGrid(Model.SearchResults, defaultSort: "DeveloperCount");
@grid.GetHtml(
    tableStyle: "grid",
    headerStyle: "gvHeading",
    rowStyle: "gridrow",
    alternatingRowStyle: "gridalt",
    columns: grid.Columns(
        grid.Column("DeveloperCount", "DeveloperCount")
    )
)

Adding PagedListPager

Second I added a PagedListPager and then I passed an IPagedList (SearchResults) model to PagedListPager.

Then we need to pass Page, SelectedCountryID, and SelectedStateID parameters to the Controller. For that, we need to Pass the model fields to PagedListPager as given below.

<div class="pagination">
    @Html.PagedListPager(Model.SearchResults, page => Url.Action("Index", new RouteValueDictionary()
    {
        { "Page", page },
        { "SelectedCountryID", Model.SelectedCountryID },
        { "SelectedStateID", Model.SelectedStateID },
    }),
    PagedListRenderOptions.MinimalWithPageCountText)
</div>

After Adding the PagedListPager list now let's Maintain a state of state dropdown list.

Finally to maintain the state dropdown list state I have called a rebindState() function on the $(document).ready method that will check whether or not ViewData["Selectedstate"] is “0” and depending on it will bind the State dropdown list and then it will make the state drop-down list selected.

Here is the complete code of the snippet of rebindState().

<script type="text/javascript">
    function rebindState() {
        debugger;
        if ('@ViewData["Selectedstate"]' != null) {
            $("#SelectedCountryID").val('@Model.SelectedCountryID');
            $.ajax({
                type: 'POST',
                url: '@Url.Action("GetStates")',
                dataType: 'json',
                data: {
                    id: $("#SelectedCountryID").val()
                },
                success: function (states) {
                    $.each(states, function (i, state) {
                        $("#SelectedStateID").append('<option value="' + state.Value + '">' + state.Text + '</option>');

                        if ('@ViewData["Selectedstate"]' != 0) {
                            $("#SelectedStateID").val('@ViewData["Selectedstate"]');
                        }                               
                    });
                },
                error: function (ex) {
                    alert('Failed to retrieve states.' + ex);
                }
            });
        }
    }
</script>

The following is the completed Home Controller code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DemoGridFilter.Models;
using PagedList;
namespace DemoGridFilter.Controllers
{
    public class MyhomeController : Controller
    {
        const int RecordsPerPage = 1;
        public ActionResult Index(SearchModelVM model, FormCollection fc)
        {
            BindCountriesName(model);
            if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue)
            {
                ViewData["Selectedstate"] = model.SelectedStateID;
                decimal mprice = Convert.ToDecimal(model.SelectedStateID);
                var entities = new AllSampleCodeEntities();
                var results = entities.DeveloperDetails
                    .Where(p => (p.CountryID == model.SelectedCountryID || model.SelectedCountryID == null) && (p.StateID == mprice || mprice == null))
                    .OrderBy(p => p.CountryID);
                var pageIndex = model.Page ?? 1;
                model.SearchResults = results.ToPagedList(pageIndex, RecordsPerPage);
            }
            return View("Index", model);
        }
        public void BindCountriesName(SearchModelVM model)
        {
            List<Country> listCountry = new List<Country> {
                new Country { CountryID = 0, Name = "Select" }
            };
            var entities = new AllSampleCodeEntities();
            var Countrieslist = (from ad in entities.Countries select ad).ToList();
            foreach (var item in Countrieslist)
            {
                Country objcon = new Country();
                objcon.CountryID = item.CountryID;
                objcon.Name = item.Name;
                listCountry.Add(objcon);
            }
            model.ListCountry = listCountry;
        }
        public JsonResult GetStates(string id)
        {
            if (id == null)
            {
                id = "0";
            }
            int CountryID = Convert.ToInt32(id);
            AllSampleCodeEntities objord = new AllSampleCodeEntities();
            var states = (from slist in objord.States where (slist.CountryID == CountryID) select new { slist.StateID, slist.StateName }).ToList();
            return Json(new SelectList(states, "StateID", "StateName"));
        }
    }
}

The following is the completed Index.cshtml code.

@model DemoGridFilter.Models.SearchModelVM
@using PagedList.Mvc
@{
    ViewBag.Title = "Search Developers";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<link href="~/Content/PagedList.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<style type="text/css">
    .webgrid-table {
        font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
        font-size: 1.2em;
        width: 100%;
        display: table;
        border-collapse: separate;
        border: solid 1px #98BF21;
        background-color: white;
    }
    .webgrid-table td,
    th {
        border: 1px solid #98BF21;
        padding: 3px 7px 2px;
    }
    .webgrid-header {
        background-color: #A7C942;
        color: #FFFFFF;
        padding-bottom: 4px;
        padding-top: 5px;
        text-align: left;
    }
    .webgrid-footer {}
    .webgrid-row-style {
        padding: 3px 7px 2px;
    }
    .webgrid-alternating-row {
        background-color: #EAF2D3;
        padding: 3px 7px 2px;
    }
</style>
<script type="text/javascript">
    $(document).ready(function () {
        //Dropdownlist Selectedchange event
        $("#SelectedCountryID").change(function () {
            $("#SelectedStateID").empty();
            $.ajax({
                type: 'POST',
                url: '@Url.Action("GetStates")', // we are calling json method
                dataType: 'json',
                data: { id: $("#SelectedCountryID").val() },
                success: function (states) {
                    // states contains the JSON formatted list
                    // of states passed from the controller
                    $("#SelectedStateID").append('<option value="' + "0" + '">' + "Select State" + '</option>');
                    debugger;
                    $.each(states, function (i, state) {
                        $("#SelectedStateID").append('<option value="' + state.Value + '">' + state.Text + '</option>');
                        // here we are adding option for States
                    });
                },
                error: function (ex) {
                    alert('Failed to retrieve states.' + ex);
                }
            });
            return false;
        })
    });
</script>
<script type="text/javascript">
    $(document).ready(function () { rebindState() });
</script>
<script type="text/javascript">
    function rebindState() {
        debugger;
        if ('@ViewData["Selectedstate"]' != null) {
            $("#SelectedCountryID").val('@Model.SelectedCountryID');
            $.ajax({
                type: 'POST',
                url: '@Url.Action("GetStates")',
                dataType: 'json',
                data: {
                    id: $("#SelectedCountryID").val()
                },
                success: function (states) {
                    $.each(states, function (i, state) {
                        $("#SelectedStateID").append('<option value="' + state.Value + '">' + state.Text + '</option>');
                        if ('@ViewData["Selectedstate"]' != 0) {
                            $("#SelectedStateID").val('@ViewData["Selectedstate"]');
                        }
                    });
                },
                error: function (ex) {
                    alert('Failed to retrieve states.' + ex);
                }
            });
        }
    }
</script>
<div class="container">
    @using (Html.BeginForm("Index", "Myhome", FormMethod.Get))
    {
        @Html.ValidationSummary(false)
        <div style="border-bottom: 1px solid #bbb">
            <h2>Search Developers</h2>
        </div>
        <table class="table">
            <tr>
                <td>
                    <div class="editor-label">
                        @Html.Label("Country")
                    </div>
                    <div class="editor-field">
                        @Html.DropDownListFor(model => model.SelectedCountryID, new SelectList(Model.ListCountry, "CountryID", "Name"))
                    </div>
                </td>
                <td>
                    <div class="editor-label">
                        @Html.Label("States")
                    </div>
                    <div class="editor-field">
                        @Html.DropDownList("SelectedStateID", new SelectList(string.Empty, "StateID", "StateName"), "Select State", new { style = "width:250px", @class = "dropdown1" })
                    </div>
                </td>
                <td style="vertical-align: bottom;">
                    <input name="SearchButton" type="submit" value="Search" />
                </td>
            </tr>
        </table>
        <div class="table table-bordered .table-striped">
            @if (Model.SearchResults != null && Model.SearchResults.Count > 0)
            {
                var grid = new WebGrid(Model.SearchResults, defaultSort: "DeveloperCount");
                @grid.GetHtml(
                    tableStyle: "webgrid-table",
                    headerStyle: "webgrid-header",
                    footerStyle: "webgrid-footer",
                    alternatingRowStyle: "webgrid-alternating-row",
                    selectedRowStyle: "webgrid-selected-row",
                    rowStyle: "webgrid-row-style",
                    mode: WebGridPagerModes.All,
                    columns: grid.Columns(
                        grid.Column("DeveloperCount", "DeveloperCount")))
            }
        </div>
        @if (Model.SearchResults != null && Model.SearchResults.Count > 0)
        {
            @Html.PagedListPager(Model.SearchResults, page => Url.Action("Index", new RouteValueDictionary()
            {
                { "Page", page },
                { "SelectedCountryID", Model.SelectedCountryID },
                { "SelectedStateID", Model.SelectedStateID },
            }), PagedListRenderOptions.PageNumbersOnly)
        }
    }
</div>

The following is the completed WebGrid code.

@if (Model.SearchResults != null && Model.SearchResults.Count > 0)
{
    var grid = new WebGrid(Model.SearchResults, defaultSort: "DeveloperCount");
    @grid.GetHtml(
        tableStyle: "grid",
        headerStyle: "gvHeading",
        rowStyle: "gridrow",
        alternatingRowStyle: "gridalt",
        columns: grid.Columns(
            grid.Column("DeveloperCount", "DeveloperCount"),
            grid.Column("DevID")
        )
    )
    <div class="pagination">
        @Html.PagedListPager(Model.SearchResults, page => Url.Action("Index", new RouteValueDictionary()
        {
            { "Page", page },
            { "SelectedCountryID", Model.SelectedCountryID },
            { "SelectedStateID", Model.SelectedStateID },
        }),
        PagedListRenderOptions.MinimalWithPageCountText)
    </div>
}

Final output

Here I have selected the country India and the state Andhra Pradesh and clicked on the Search button. Then it brought all the Developer-related data of Andhra Pradesh into the grid and below you can see the Pager list.

The following is the snapshot of the grid displaying the first record:

Search developers

The following is the snapshot of the grid displaying the second record.

Displaying second record

Here in the preceding snapshot, you can see we have 2 dropdown lists and after paging, it still maintains the state of the dropdown list.

Conclusion

Finally, we have completed the learning of the Filter WebGrid with Cascading Dropdownlist along with Paging in MVC.


Similar Articles